package net.kleinhenz.norabase.server;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.Query;

import net.kleinhenz.norabase.client.BattlegroupService;
import net.kleinhenz.norabase.server.model.Account;
import net.kleinhenz.norabase.server.model.RuneEntry;
import net.kleinhenz.norabase.shared.model.Battlegroup;
import net.kleinhenz.norabase.shared.model.Champion;
import net.kleinhenz.norabase.shared.model.Equipment;
import net.kleinhenz.norabase.shared.model.Relic;
import net.kleinhenz.norabase.shared.model.Rune;
import net.kleinhenz.norabase.shared.model.Spell;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;

public class BattlegroupServiceImpl extends RemoteServiceServlet implements
		BattlegroupService {

	private static final long serialVersionUID = 1L;

	@Override
	public String createBattlegroup(Battlegroup<Rune> bg) {

		EntityManager em = EMF.get().createEntityManager();
		Account account = retrieveAccount(em);
		if (account==null)
			return null;
		
		net.kleinhenz.norabase.server.model.Battlegroup sBG = new net.kleinhenz.norabase.server.model.Battlegroup();
		sBG.setDesc(bg.getDesc());
		sBG.setName(bg.getName());
		sBG.setStrat(bg.getStrat());
		sBG.setPublicBattlegroup(bg.isPublicBattlegroup());
		sBG.setAccountId(LoginServiceImpl.getUser());
		for (Rune r : bg.getRunes()) {
			if (r instanceof Champion)
				sBG.getRunes().add(new RuneEntry(r.getId(), Rune.CHAMPION));		
			else if (r instanceof Equipment)
				sBG.getRunes().add(new RuneEntry(r.getId(), Rune.EQUIPMENT));
			else if (r instanceof Spell)
				sBG.getRunes().add(new RuneEntry(r.getId(), Rune.SPELL));
			else if (r instanceof Relic)
				sBG.getRunes().add(new RuneEntry(r.getId(), Rune.RELIC));
		}
		
		account.getBattlegroups().add(sBG);
		em.persist(sBG);
		em.merge(account);
		
		em.close();
		return KeyFactory.keyToString(sBG.getId());
	}

	@Override
	public Battlegroup<Rune> retrieveBattlegroup(String id) {

		if (id==null)
			return null;

		EntityManager em = EMF.get().createEntityManager();
		Account account = retrieveAccount(em);
		
		Battlegroup<Rune> bg = retrieveBattlegroup(id, account, em);
		
		em.close();		
		return bg;
	}

	@Override
	public List<Battlegroup<Rune>> retrieveBattlegroups() {
				
		EntityManager em = EMF.get().createEntityManager();
		Account account = retrieveAccount(em);
		if (account==null)
			return Collections.emptyList();
		
		List<Battlegroup<Rune>> bgl = new ArrayList<Battlegroup<Rune>>();
		for (net.kleinhenz.norabase.server.model.Battlegroup sBG : account.getBattlegroups())
			bgl.add(retrieveBattlegroup(sBG.getId(), account, em));
		
		em.close();
		return bgl;
	}

	@Override
	public void updateBattlegroup(Battlegroup<Rune> battlegroup) {

		EntityManager em = EMF.get().createEntityManager();
		Account account = retrieveAccount(em);
		net.kleinhenz.norabase.server.model.Battlegroup sBG = em.find(net.kleinhenz.norabase.server.model.Battlegroup.class, KeyFactory.stringToKey(battlegroup.getId()));
		if (account!=null && sBG!=null) {
			sBG.setName(battlegroup.getName());
			sBG.setDesc(battlegroup.getDesc());
			sBG.setStrat(battlegroup.getStrat());
			sBG.setPublicBattlegroup(battlegroup.isPublicBattlegroup());
			sBG.setAccountId(LoginServiceImpl.getUser());
			// FIXME: think about syncing the runes list here
			em.merge(sBG);
		}		
		em.close();
	}

	@Override
	public void deleteBattlegroup(Battlegroup<Rune> battlegroup) {

		EntityManager em = EMF.get().createEntityManager();
		Account account = retrieveAccount(em);
		net.kleinhenz.norabase.server.model.Battlegroup sBG = em.find(net.kleinhenz.norabase.server.model.Battlegroup.class, KeyFactory.stringToKey(battlegroup.getId()));
		// FIXME: check if battlegroup is owned by user
		if (account!=null && sBG!=null) {
			account.getBattlegroups().remove(sBG);
			em.merge(account);
			em.remove(sBG);
		}
			
		em.close();
	}

	public static Account retrieveAccount(EntityManager em) {

		String user = LoginServiceImpl.getUser();
		if (user==null || user.isEmpty())
			return null;

		Query q = em.createQuery("select from " + net.kleinhenz.norabase.server.model.Account.class.getName() + " where email=:email");
		q.setParameter("email", user);

		Object o = q.getSingleResult();
		if (o!=null)
			return (Account)o;
		else
			// can not happen, accounts are created on login implicitly
			throw new RuntimeException("Account not found: " + user);
	}
	
	private Battlegroup<Rune> retrieveBattlegroup(String id, Account account, EntityManager em) {

		Key key = KeyFactory.stringToKey(id);
		return retrieveBattlegroup(key, account, em);
	}	
	
	private Battlegroup<Rune> retrieveBattlegroup(Key id, Account account, EntityManager em) {

		net.kleinhenz.norabase.server.model.Battlegroup sBG = em.find(net.kleinhenz.norabase.server.model.Battlegroup.class, id);
		if (sBG==null || !account.getBattlegroups().contains(sBG))
			return null;
		
		Battlegroup<Rune> bg = new Battlegroup<Rune>();
		bg.setId(KeyFactory.keyToString(sBG.getId()));
		bg.setDesc(sBG.getDesc());
		bg.setName(sBG.getName());
		bg.setStrat(sBG.getStrat());
		bg.setPublicBattlegroup(sBG.isPublicBattlegroup());
		bg.setAccountId(sBG.getAccountId());
		for (RuneEntry r : sBG.getRunes()) {
			bg.getRunes().add(retrieveRune(r.getRuneId(), r.getType()));			
		}
		return bg;
	}

	Rune retrieveRune(Integer id, String type) {
		
		return PoxNoraXMLReader.getRune(id, type);
	}
}
